// Graph node implementation
use codegraph_core::{
    CodeNode, CodeEdge, NodeId, NodeType, SourceLocation, NodeMetadata, EdgeType, EdgeId, Result,
};
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::hash::{Hash, Hasher};

/// Enhanced graph node with rich metadata and relationship tracking
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphNode {
    /// Unique identifier for the node
    id: NodeId,
    /// Type of the code element
    node_type: NodeType,
    /// Name/identifier of the code element
    name: String,
    /// Source location information
    location: SourceLocation,
    /// Programming language
    language: String,
    /// Rich metadata about the node
    metadata: NodeMetadata,
    /// Cached edge references for performance
    edge_cache: HashMap<EdgeType, Vec<EdgeId>>,
    /// Node-specific attributes
    attributes: HashMap<String, String>,
    /// Semantic information
    semantic_info: SemanticInfo,
    /// Creation and modification timestamps
    timestamps: NodeTimestamps,
}

/// Semantic information about the code element
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SemanticInfo {
    /// Documentation/comments associated with the element
    pub documentation: Option<String>,
    /// Visibility/access level (public, private, etc.)
    pub visibility: Option<String>,
    /// Modifiers (static, final, abstract, etc.)
    pub modifiers: Vec<String>,
    /// Type signature (for functions, variables, etc.)
    pub type_signature: Option<String>,
    /// Parameters (for functions/methods)
    pub parameters: Vec<Parameter>,
    /// Return type (for functions/methods)
    pub return_type: Option<String>,
    /// Complexity metrics
    pub complexity: ComplexityMetrics,
}

/// Parameter information for functions/methods
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Parameter {
    /// Parameter name
    pub name: String,
    /// Parameter type
    pub param_type: Option<String>,
    /// Default value
    pub default_value: Option<String>,
    /// Whether the parameter is optional
    pub is_optional: bool,
}

/// Complexity metrics for code elements
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplexityMetrics {
    /// Cyclomatic complexity
    pub cyclomatic: u32,
    /// Cognitive complexity
    pub cognitive: u32,
    /// Lines of code
    pub lines_of_code: u32,
    /// Number of parameters (for functions)
    pub parameter_count: u32,
    /// Nesting depth
    pub nesting_depth: u32,
}

/// Timestamps for tracking node lifecycle
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeTimestamps {
    /// When the node was created
    pub created_at: chrono::DateTime<chrono::Utc>,
    /// When the node was last modified
    pub modified_at: chrono::DateTime<chrono::Utc>,
    /// When the node was last accessed
    pub accessed_at: chrono::DateTime<chrono::Utc>,
}

impl Default for SemanticInfo {
    fn default() -> Self {
        Self {
            documentation: None,
            visibility: None,
            modifiers: Vec::new(),
            type_signature: None,
            parameters: Vec::new(),
            return_type: None,
            complexity: ComplexityMetrics::default(),
        }
    }
}

impl Default for ComplexityMetrics {
    fn default() -> Self {
        Self {
            cyclomatic: 1,
            cognitive: 0,
            lines_of_code: 0,
            parameter_count: 0,
            nesting_depth: 0,
        }
    }
}

impl GraphNode {
    /// Create a new graph node
    pub fn new(
        id: NodeId,
        node_type: NodeType,
        name: String,
        location: SourceLocation,
        language: String,
    ) -> Self {
        let now = chrono::Utc::now();
        Self {
            id,
            node_type,
            name,
            location,
            language,
            metadata: NodeMetadata::default(),
            edge_cache: HashMap::new(),
            attributes: HashMap::new(),
            semantic_info: SemanticInfo::default(),
            timestamps: NodeTimestamps {
                created_at: now,
                modified_at: now,
                accessed_at: now,
            },
        }
    }

    /// Create a new node with semantic information
    pub fn with_semantic_info(
        id: NodeId,
        node_type: NodeType,
        name: String,
        location: SourceLocation,
        language: String,
        semantic_info: SemanticInfo,
    ) -> Self {
        let mut node = Self::new(id, node_type, name, location, language);
        node.semantic_info = semantic_info;
        node
    }

    /// Get semantic information
    pub fn semantic_info(&self) -> &SemanticInfo {
        &self.semantic_info
    }

    /// Update semantic information
    pub fn update_semantic_info(&mut self, semantic_info: SemanticInfo) {
        self.semantic_info = semantic_info;
        self.timestamps.modified_at = chrono::Utc::now();
    }

    /// Get node attributes
    pub fn attributes(&self) -> &HashMap<String, String> {
        &self.attributes
    }

    /// Set an attribute
    pub fn set_attribute(&mut self, key: String, value: String) {
        self.attributes.insert(key, value);
        self.timestamps.modified_at = chrono::Utc::now();
    }

    /// Get an attribute
    pub fn get_attribute(&self, key: &str) -> Option<&String> {
        self.attributes.get(key)
    }

    /// Remove an attribute
    pub fn remove_attribute(&mut self, key: &str) -> Option<String> {
        let result = self.attributes.remove(key);
        if result.is_some() {
            self.timestamps.modified_at = chrono::Utc::now();
        }
        result
    }

    /// Update access timestamp
    pub fn mark_accessed(&mut self) {
        self.timestamps.accessed_at = chrono::Utc::now();
    }

    /// Get timestamps
    pub fn timestamps(&self) -> &NodeTimestamps {
        &self.timestamps
    }

    /// Cache edge reference for performance
    pub fn cache_edge(&mut self, edge_type: EdgeType, edge_id: EdgeId) {
        self.edge_cache
            .entry(edge_type)
            .or_insert_with(Vec::new)
            .push(edge_id);
    }

    /// Remove edge from cache
    pub fn uncache_edge(&mut self, edge_type: EdgeType, edge_id: EdgeId) {
        if let Some(edges) = self.edge_cache.get_mut(&edge_type) {
            edges.retain(|&id| id != edge_id);
        }
    }

    /// Get cached edges by type
    pub fn cached_edges(&self, edge_type: EdgeType) -> Vec<EdgeId> {
        self.edge_cache
            .get(&edge_type)
            .map(|edges| edges.clone())
            .unwrap_or_default()
    }

    /// Clear edge cache
    pub fn clear_edge_cache(&mut self) {
        self.edge_cache.clear();
    }
}

impl CodeNode for GraphNode {
    fn id(&self) -> NodeId {
        self.id
    }

    fn node_type(&self) -> &NodeType {
        &self.node_type
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn location(&self) -> &SourceLocation {
        &self.location
    }

    fn language(&self) -> &str {
        &self.language
    }

    fn metadata(&self) -> &NodeMetadata {
        &self.metadata
    }

    fn edges(&self, _edge_type: Option<&EdgeType>) -> Vec<&dyn CodeEdge> {
        // Note: This implementation returns empty since we don't store edges directly
        // in nodes anymore - they're managed by the graph structure
        Vec::new()
    }

    fn add_edge(&mut self, _edge: Box<dyn CodeEdge>) -> Result<()> {
        // Note: Edge management is now handled by the CodeGraph structure
        // This method is kept for trait compatibility but doesn't do anything
        Ok(())
    }

    fn remove_edge(&mut self, _edge_id: EdgeId) -> Result<bool> {
        // Note: Edge management is now handled by the CodeGraph structure
        // This method is kept for trait compatibility but doesn't do anything
        Ok(false)
    }

    fn update_metadata(&mut self, metadata: NodeMetadata) -> Result<()> {
        self.metadata = metadata;
        self.timestamps.modified_at = chrono::Utc::now();
        Ok(())
    }

    fn as_any(&self) -> &dyn std::any::Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
        self
    }
}

impl PartialEq for GraphNode {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for GraphNode {}

impl Hash for GraphNode {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id.hash(state);
    }
}

impl std::fmt::Display for GraphNode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}({}) [{}:{}:{}]",
            self.name,
            format!("{:?}", self.node_type),
            self.language,
            self.location.start_line,
            self.location.start_column
        )
    }
}
